﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ShevaEngine.Core.Modules.Animations
{
    /// <summary>
    /// A collection of BonePose objects that represent the bone transforms of a model
    /// as affected by animations.
    /// </summary>
    public class BonePoseCollection : ReadOnlyCollection<BonePose>
    {
        /// <summary>A dictionary for quick access to bone poses based on bone name.</summary>
        private Dictionary<string, BonePose> boneDict = new Dictionary<string, BonePose>();

        /// <summary>
        /// This class should not be externally instantiated.
        /// </summary>
        /// <param name="anims"></param>
        internal BonePoseCollection(IList<BonePose> anims)
            : base(anims)
        {
            for (int i = 0; i < anims.Count; i++)
            {
                string boneName = anims[i].BoneName;
                if (boneName != null && boneName != "" && !boneDict.ContainsKey(boneName))
                {
                    boneDict.Add(boneName, anims[i]);
                }
            }
        }

        /// <summary>
        /// Creates a set of bonepose objects from a skeleton.
        /// </summary>
        /// <param name="bones"></param>
        /// <returns></returns> 
        internal static BonePoseCollection FromModelBoneCollection(
            ModelBoneCollection bones)
        {
            BonePose[] anims = new BonePose[bones.Count];
            for (int i = 0; i < bones.Count; i++)
            {
                if (bones[i].Parent == null)
                {
                    BonePose ba = new BonePose(
                        bones[i],
                        bones,
                        anims);

                }
            }

            return new BonePoseCollection(anims);
        }

        /// <summary>
        /// Computes the absolute transforms for the collection and copies
        /// the values.
        /// </summary>
        /// <param name="transforms">The array into which the transforms will be 
        /// copied.</param>
        public void CopyAbsoluteTransformsTo(Matrix[] transforms)
        {
            for (int i = 0; i < transforms.Length; i++)
            {
                if (i > 0) // not root
                {
                    // This works because the skeleton is always flattened;
                    // the parent index is always lower than the child index.
                    Matrix curTransform = this[i].GetCurrentTransform();
                    Matrix parentTransform = transforms[this[i].Parent.BoneIndex];
                    Vector3 currentTranslation = curTransform.Translation;
                    Matrix parentRotation = Matrix.CreateFromQuaternion(
                        Quaternion.CreateFromRotationMatrix(parentTransform));
                    Matrix currentRotation = Matrix.CreateFromQuaternion(
                        Quaternion.CreateFromRotationMatrix(curTransform));

                    currentTranslation = Vector3.Transform(currentTranslation,
                        parentRotation);
                    currentTranslation += parentTransform.Translation;
                    currentTranslation = parentTransform.Translation + curTransform.Translation;

                    //                    transforms[i] = currentRotation * parentRotation;
                    transforms[i] = curTransform * parentTransform;
                }
                else
                {
                    transforms[i] = this[i].GetCurrentTransform();
                }
            }
        }


        /// <summary>
        /// Gets a BonePose object.
        /// </summary>
        /// <param name="boneName">The name of the bone for which the BonePose 
        /// will be returned.</param>
        /// <returns>The BonePose associated with the bone name.</returns>
        public BonePose this[string boneName]
        {
            get { return boneDict[boneName]; }
        }
    }
}
